# DESCRIPTION
# 
#   Driver that facilitates the testing of different implementations of a 
#   two-dimensional discrete convolution filter.
# 
# HISTORY
# 
#   20100921 KP - Initial version
# 
# AUTHOR
# 
#   Koen Poppe, Department of Computer Science,
#   Katholieke Universiteit Leuven, Celestijnenlaan 200A,
#   B-3001 Heverlee, Belgium
#   Email:  Koen.Poppe@cs.kuleuven.be
#

# Used modules
import numpy
import pylab
import matplotlib
import matplotlib.pyplot
import matplotlib.cm
import timeit

# Settings
is_timing    = True
show_figures = True and not(is_timing) # certainly don't show figures while timing

# Read the image and keep only luminance information
img = pylab.imread('Flowers-large.png') # small, normal or large
img = 0.3*img[:,:,0] + 0.59*img[:,:,1] + 0.11*img[:,:,2]

# The filters that will by applied, wrapped in a structure
class Filter(object):
    def __init__(self,plot_index,name,filter):
        self.plot_index=plot_index
        self.name = name
        self.filter = filter
filters = [
    Filter( 1, 'Smoother 3x3',              numpy.ones([3,3])              ),
    Filter( 2, 'Smoother 5x5',              numpy.ones([5,5])              ),
    Filter( 3, 'Vertical edge detection',   [[-1, 0,-1],[ 2, 0, 2],[-1, 0,-1]] ),
    Filter( 6, 'Horizontal edge detection', [[-1, 2,-1],[ 0, 0, 0],[-1, 2,-1]] ),
    Filter( 9, 'Edge detector',             [[ 1, 1, 1],[ 1,-8, 1],[ 1, 1, 1]] ),
    Filter( 4, 'Emboss North-West',         [[ 0,-1,-2],[ 1, 1,-1],[ 2, 1, 0]] ),
    Filter( 7, 'Emboss South-East',         [[ 0, 1, 2],[-1, 1, 1],[-2,-1, 0]] ),
    Filter( 5, 'Identity',                  [[ 0, 0, 0],[ 0, 1, 0],[ 0, 0, 0]] ),
    Filter( 8, 'High pass filter',          [[0,-1,-1,-1,0],[-1,2,-4,2,-1],[-1,-4,13,-4,-1],[-1,2,-4,2,-1],[0,-1,-1,-1,0]] )
]

# The implementations that will be tested
import imageconvolution_c
import imageconvolution_c_opt
import imageconvolution_fortran
import imageconvolution_fortran_opt
import imageconvolution_python
implementations = {
    'C       (-O0)' : imageconvolution_c.imageconvolution,
    'C       (-O4)' : imageconvolution_c_opt.imageconvolution,
    'Fortran (-O0)' : imageconvolution_fortran.imageconvolution,
    'Fortran (-O4)' : imageconvolution_fortran_opt.imageconvolution,
    'Python native' : imageconvolution_python.imageconvolution
}

# Array storage orders 'C' (row-wise, C/C++) or 'F' (column-wise, Fortran)
# (only tested when in timing mode)
if is_timing:
    orders=['C','F']
else:
    orders=['C']

for order in orders:
    
    # Convert all matrices to correct floating point type and order
    img = numpy.array( img, dtype=numpy.float32, order=order )
    for i in range(len(filters)):
        filters[i].filter = numpy.array( filters[i].filter, dtype=numpy.float32, order=order)
    
    # Time or run all the implementations
    timings = {};
    for (implementation,imageconvolution) in implementations.iteritems():
        
        # Determine number of repetitions and report the progress
        if is_timing:
            nb_repeat = 10
            print "- testing %(name)s with %(repeat)i repetitions..." % \
                { 'name': implementation, 'repeat': nb_repeat }
            tic = timeit.time.time()
        else:
            nb_repeat = 1
            print "- running %(name)s" % \
                { 'name': implementation }
        
        # Iterate over all the filters and store the filtered images
        filtered_imgs = [None]*len(filters) # pre-allocation
        for repeat in range(nb_repeat):
            for i in range(len(filters)):
                filtered_imgs[i] = imageconvolution( img, filters[i].filter )
        
        # Store timings
        if is_timing:
            toc = timeit.time.time()
            timings[(toc-tic)] = implementation
        
        # Show the filtered images
        if show_figures:    
            fig = matplotlib.pyplot.figure()
            fig.text(0.5,0.98,implementation,ha='center',va='top',color='blue',weight='bold')
            for i in range(len(filters)):
                ax = fig.add_subplot(3,3,filters[i].plot_index)
                ax.set_title( filters[i].name,fontsize=10)
                ax.imshow( filtered_imgs[i], cmap=matplotlib.cm.gray )
                ax.set_xticks(()) # remove x-ticks
                ax.set_yticks(()) # remove y-ticks
    
    # Report timings
    if is_timing:
        print "\n"
        print "      TIMINGS COMPARISON (%(order)s-order)" % { 'order': order }
        print "     ------------------------------"
        for time in sorted(timings.iterkeys()): # Sort implementations on ascending times
            print "      - %(name)s: %(time)8.3f s" % \
                { 'name':timings[time], 'time':time }
        print "     ------------------------------\n"


    
# Show the figures
if show_figures:
    matplotlib.pyplot.show() # does not return before all images are closed!
